package top.eggcode.common.auth;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import top.eggcode.common.lang.DateUtil;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Title: token生成器
 * Description: TODO
 * Date: 2021/4/10 15:46
 *
 * @author JiaQi Ding
 * @version 1.0
 */
public class SessionTokenUtil {

    /**
     * token 加密类型
     */
    static final String ALG = "HS256";
    /**
     * token 类型，自定义
     */
    static final String TYP = "session";
    private static final Logger logger = LogManager.getLogger(SessionTokenUtil.class);
    private static final int MINUTE = 60;
    private static final int HOUR = 3600;
    /**
     * 刷新时间，提前半小时通知客户端
     */
    private static final int REFRESH_TIME = 30;
    private static final int SECOND = 1;
    /**
     * 颁发机构，自定义
     */
    private static final String ISSUER = "sense";
    private static final String SECRET = ".,l;p[df";

    /**
     * 申请令牌
     * 存活时间：2小时
     *
     * @param username 用户名
     * @param secret   密匙
     * @return 字符串令牌
     */
    public static String apply(String username, String secret) {
        // 存活时间：2小时
        int liveTime = MINUTE * 60;

        return apply(username, liveTime);
    }

    /**
     * 申请令牌
     *
     * @param username 用户名
     * @param liveTime 存活时间
     * @return 字符串令牌
     */
    public static String apply(String username, int liveTime) {

        try {
            // 设置令牌头部信息
            Map<String, Object> header = new HashMap<>(10);
            header.put("alg", ALG);
            header.put("typ", TYP);
            // 密匙 id
            header.put("kid", "");

            Calendar rightNow = Calendar.getInstance();
            // 设置令牌到期时间
            rightNow.add(Calendar.SECOND, liveTime);
            // 获得令牌到期时间
            Date expirationDate = rightNow.getTime();

            // 算法签名
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            return JWT.create()
                    .withIssuer(ISSUER)
                    .withHeader(header)
                    .withClaim("username", username)
                    .withExpiresAt(expirationDate)
                    .sign(algorithm);
        } catch (JWTCreationException exception) {
            //Invalid Signing configuration / Couldn't convert Claims.
            // 签名无效
            return "";
        }
    }

    /**
     * 验证令牌有效性
     *
     * @param token 令牌
     * @return 布尔值
     * @throws JWTVerificationException token 解析错误
     */
    public static boolean verify(String token) {
        try {
            // 算法签名
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            // 核验器
            JWTVerifier verifier = JWT.require(algorithm).build();
            verifier.verify(token);
            // 没发生异常就说明令牌未过期
            return true;
        } catch (JWTVerificationException exception) {
            // 无效token
            return false;
        }
    }

    /**
     * 是否过期
     * 令牌无效时返回 false
     */
    public static boolean overdue(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            // 获取过期时间
            Date deadline = jwt.getExpiresAt();
            if (deadline.before(new Date())) {
                return true;
            }
        } catch (JWTDecodeException exception) {
            //Invalid token
            // 令牌解码失败
        }
        return false;
    }

    /**
     * 是否需要刷新令牌
     *
     * @param token 令牌
     * @return 是/否
     */
    public static boolean requiredRefresh(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            // 获取过期时间
            Date deadline = jwt.getExpiresAt();
            // 提前半小时告知客户端需要刷新 token
            if (deadline.before(DateUtil.addDateMinutes(new Date(), REFRESH_TIME))) {
                return true;
            }
        } catch (JWTDecodeException exception) {
            //Invalid token
            // 令牌解码失败
        }
        return false;
    }


    /**
     * 获取用户名
     *
     * @param token 令牌
     * @return username
     */
    public static String getUsername(String token) {
        // 先验证令牌有效性
        try {
            DecodedJWT jwt = JWT.decode(token);
            // 获取属性值
            Claim claim = jwt.getClaim("username");

            return claim.asString();
        } catch (JWTDecodeException exception) {
            //Invalid token
            // 令牌解码失败
        }
        return "";
    }

    /**
     * 获取指定参数值
     *
     * @param token 令牌
     * @param name  参数名称
     * @return 参数值
     */
    public static String getParameter(String token, String name) {
        // 先验证令牌有效性
        try {
            DecodedJWT jwt = JWT.decode(token);
            // 获取属性值
            Claim claim = jwt.getClaim(name);
            return claim.asString();
        } catch (JWTDecodeException exception) {
            //Invalid token
            // 令牌解码失败
        }
        return "";
    }

}